﻿#region

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using RGG.sharp;

#endregion

namespace RGG
{
    public class RggManager : IDisposable
    {
        private readonly Random _random = new Random((int) DateTime.Now.Ticks%int.MaxValue);
        private RussianGridGameConfig _config;
        /// <summary>
        ///     指示是否已经画了网格线
        /// </summary>
        private bool _drawedCurrentGbGridLine;
        /// <summary>
        ///     指示是否已经画了网格线
        /// </summary>
        private bool _drawedNextGbGridLine;
        private Dictionary<Keys, KeyDownDirection> _keyMap;
        private Point _originDrawPoint;
        private Size _originSize;
        private Timer _rggTimer = new Timer();
        private RggState _state = RggState.Stop;

        /// <summary>
        ///     初始化一个游戏管理器类
        /// </summary>
        /// <param name="pictureBox">显示游戏的图片空间</param>
        /// <param name="nextGbPictureBox">显示下一张图片的图片控件</param>
        /// <param name="KeyMapIndex"></param>
        public RggManager(PictureBox pictureBox, PictureBox nextGbPictureBox, int KeyMapIndex = 0)
        {
            RggPictureBox = pictureBox;
            NextgbPictureBox = nextGbPictureBox;
            Config = RussianGridGameConfig.GetInstance(KeyMapIndex);
            GbFactory = new GeometricBodyFactory(new int[Config.Width, Config.Height, 2]);
            KeyMap = Config.CurrentKeyMap.ToDictionary(
                key => (Keys) Enum.Parse(typeof (Keys), key.Key),
                value => (KeyDownDirection) Enum.Parse(typeof (KeyDownDirection), value.Value)
                );
            Score = 0;
            CurrentGeometricBody = GbFactory.GetSharp(_random.Next());
            _originDrawPoint = new Point(CurrentGeometricBody.DrawLeft, CurrentGeometricBody.DrawTop);
            _originSize = new Size(CurrentGeometricBody.Width, CurrentGeometricBody.Height);
            NextGeometricBody = GbFactory.GetSharp(_random.Next());
        }

        public PictureBox RggPictureBox { get; set; }
        public PictureBox NextgbPictureBox { get; set; }
        public GeometricBody CurrentGeometricBody { get; private set; }
        internal RussianGridGameConfig Config
        {
            get { return _config; }
            private set { _config = value; }
        }
        /// <summary>
        ///     返回下一个几何体
        /// </summary>
        public GeometricBody NextGeometricBody { get; private set; }
        public int Score { get; private set; }
        public GeometricBodyFactory GbFactory { get; private set; }
        public RggState State
        {
            private set { _state = value; }
            get { return _state; }
        }
        public Timer RggTimer
        {
            get { return _rggTimer; }
            private set { _rggTimer = value; }
        }
        public Dictionary<Keys, KeyDownDirection> KeyMap
        {
            get { return _keyMap; }
            private set { _keyMap = value; }
        }

        public void Dispose()
        {
            StopGame();
        }

        public void StartGame()
        {
            if (State == RggState.Pause)
            {
                State = RggState.Start;
            }
            else if (State == RggState.Stop || State == RggState.GameOver)
            {
                Score = 0;
                State = RggState.Start;
                _rggTimer.Interval = Config.Speed;
                _rggTimer.Tick += _rggTimer_Tick;
                _rggTimer.Start();
            }
        }

        private void _rggTimer_Tick(object sender, EventArgs e)
        {
            if (State == RggState.Start)
            {
                if (!CurrentGeometricBody.KeyDown(KeyDownDirection.Down))
                {
                    var newGeometricBody = GbFactory.GetSharp(_random.Next());
                    if (CurrentGeometricBody.DrawTop <= newGeometricBody.DrawTop + newGeometricBody.Height)
                    {
                        State = RggState.GameOver;
                        return;
                    }
                    CurrentGeometricBody = NextGeometricBody;
                    NextGeometricBody = newGeometricBody;
                    _originDrawPoint.X = CurrentGeometricBody.DrawLeft;
                    _originDrawPoint.Y = CurrentGeometricBody.DrawTop;
                    _originSize.Width = CurrentGeometricBody.Width;
                    _originSize.Height = CurrentGeometricBody.Height;
                    //NextgbPictureBox.Refresh();
                }
                ClearLine();
            }
        }

        public void StopGame()
        {
            RggTimer.Stop();
            State = RggState.Stop;
        }

        public void PauseGame()
        {
            State = RggState.Pause;
        }

        /// <summary>
        ///     清除一整行的方块
        /// </summary>
        private void ClearLine()
        {
            int[] hang = CanClearLine();
            if (hang!=null)
            {
                for (int i = 0; i < hang.Length; ++i)
                {
                    for (int j = hang[i]; j >= 0; --j)
                    {
                        bool hasDot = false;
                        if (j != 0)
                        {
                            for (int k = 0; k < GbFactory.LargeDotMatrix.GetLength(1); ++k)
                            {
                                GbFactory.SetDot(j, k, GbFactory.LargeDotMatrix[j - 1, k, 0], GbFactory.GetColor(j - 1, k));
                                if (GbFactory.LargeDotMatrix[j, k, 0] != 0)
                                {
                                    hasDot = true;
                                }
                            }
                        }
                        else
                        {
                            for (int k = 0; k < GbFactory.LargeDotMatrix.GetLength(1); ++k)
                            {
                                GbFactory.SetDot(0, k, 0, 0);
                            }
                        }
                        if (!hasDot)
                        {
                            break;
                        }
                    }
                    RggPictureBox.Refresh();
                }
                AddScore(hang);
            }
        }

        /// <summary>
        /// 查看是否可以消除一行或多行
        /// </summary>
        /// <returns>返回可以消除行的数组或null</returns>
        private int[] CanClearLine()
        {
            int[] hang = new int[4];
            int index = 0;
            bool hasSquare;
            int width  = GbFactory.LargeDotMatrix.GetLength(0) - 1;
            for (int i = GbFactory.LargeDotMatrix.GetLength(1) - 1; i > 0; --i)
            {
                hasSquare = false;
                int j = 0;
                for (j = 0; j < width; ++j)
                {
                    if (GbFactory.IsExist(i, j))
                    {
                        hasSquare = true;
                    }
                }
                if (j == width)
                {
                    hang[index++] = i;
                    if (index == 4) break;
                }
                if (!hasSquare)
                {
                    break;
                }
            }
            if (index == 0)
            {
                return null;
            }
            else
            {
                int[] newHang = new int[index];
                for (int i = 0; i < index; ++i)
                {
                    newHang[i] = hang[i];
                }
                return newHang;
            }
        }
        /// <summary>
        /// 根据消去的行加分
        /// </summary>
        /// <param name="clearedLine"></param>
        private void AddScore(int[] clearedLine)
        {
            if (clearedLine == null || clearedLine.Length == 0) return;
            int continuousLineNumber = 0;
            for (int i = 0; i < clearedLine.Length; ++i)
            {
                if (continuousLineNumber == 0)
                {
                    ++continuousLineNumber;
                }
                else if (Math.Abs(clearedLine[i] - clearedLine[i - 1]) == 1)
                {
                    ++continuousLineNumber;
                }
                else
                {
                    Score += Config.Scores[continuousLineNumber];
                    continuousLineNumber = 0;
                }

            }
        }

        /// <summary>
        ///     将图片画到PictureBox上面，更新图片
        /// </summary>
        public void RedrawCurrentGb(Graphics g)
        {
            Size pbSize = RggPictureBox.Size;
            //每个小格所占用的像素，要求两个值相等
            int iPixelPerCompartmentWidth = pbSize.Width / Config.Width;
            int iPixelPerCompartmentHeight = pbSize.Height / Config.Height;
            Debug.Assert(iPixelPerCompartmentWidth == iPixelPerCompartmentHeight, "要求两个值相等，即每个小格为正方形");

            //TODO：代码可以优化一下，例如每一次只会更改一小部分，不要求全部重绘
            for (int i = Config.Height - 1; i >= 0; --i)
            {
                int y = i * iPixelPerCompartmentHeight;
                for (int j = 0; j < Config.Width; ++j)
                {
                    Color currColor;
                    if (GbFactory.IsExist(j, i))
                    {
                        currColor = GbFactory.GetColor(j, i);
                    }
                    else
                    {
                        currColor = Config.CurrentGbBkColor;
                    }
                    Brush brush = new SolidBrush(currColor);
                    Pen pen = new Pen(brush);
                    Rectangle rect = new Rectangle(
                        j * iPixelPerCompartmentWidth,
                        y,
                        iPixelPerCompartmentWidth - 2 * Config.CurrentVerticalGridLineWidth,
                        iPixelPerCompartmentHeight - 2 * Config.CurrentHorizontalGridLineWidth
                        );
                    g.DrawRectangle(pen, rect);
                    g.FillRectangle(brush, rect);
                    brush.Dispose();
                    pen.Dispose();
                }
            }//*/
            DrawString(g);
        }        

        public void RedrawNextGb(Graphics g)
        {
            if (!_drawedCurrentGbGridLine) DrawNextGeometricBodyGridLine(g);
            List<int> dotMatrix = NextGeometricBody.DotMatrix;
            g.FillRectangle(new SolidBrush(Config.NextGbBkColor), 0, 0, NextgbPictureBox.Size.Width,
                NextgbPictureBox.Size.Height);
            for (int i = 0; i < NextGeometricBody.Width; ++i)
            {
                for (int j = 0; j < NextGeometricBody.Height; ++j)
                {
                    Rectangle rect = new Rectangle(
                        j*Config.NextPerCellSize,
                        i*Config.NextPerCellSize,
                        Config.NextPerCellSize - 2*Config.NextVerticalGridLineWidth,
                        Config.NextPerCellSize - 2*Config.NextHorizontalGridLineWidth
                        );
                    Color currentColor;
                    if (dotMatrix[j*NextGeometricBody.Width + i] != 0)
                    {
                        currentColor = NextGeometricBody.ColorGB;
                    }
                    else
                    {
                        currentColor = Config.NextGbBkColor;
                    }
                    using (Pen pen = new Pen(currentColor))
                    {
                        using (Brush brush = new SolidBrush(currentColor))
                        {
                            g.DrawRectangle(pen, rect);
                            g.FillRectangle(brush, rect);
                        }
                    }
                }
            }
        }

#if DEBUG
        public void ShowCurrentGbToolTip(ToolTip tt, Point point)
        {
            const string formatString = "({0},{1})";
            string tip = "";
            int x = point.X/Config.CurrentPerCellSize;
            int y = point.Y/Config.CurrentPerCellSize;
            tip = string.Format(formatString, x, y);
            tt.SetToolTip(RggPictureBox, "nextGb");
            tt.Show(tip, RggPictureBox, point);
        }
#endif

        /// <summary>
        ///     将Game Over消息传递给用户
        /// </summary>
        private void GameOver()
        {
            State = RggState.GameOver;
        }

        /// <summary>
        ///     在PictureBox上面画一些字符串，例如GameOver之类
        /// </summary>
        /// <param name="g"></param>
        private void DrawString(Graphics g)
        {
            string strToDraw = "";
            switch (State)
            {
                case RggState.GameOver:
                    strToDraw = "Game Over!";
                    break;
                case RggState.Pause:
                    strToDraw = "Pause";
                    break;
            }
            if (!strToDraw.Equals(""))
            {
                g.DrawString(strToDraw, new Font(FontFamily.GenericSansSerif, 24F), new SolidBrush(Color.BlueViolet),
                    new PointF(x: 0, y: RggPictureBox.Size.Height/2));
            }
        }

        public bool KeyDown(Keys key)
        {
            if (KeyMap.ContainsKey(key))
            {
                bool result =  CurrentGeometricBody.KeyDown(KeyMap[key]);
                    RggPictureBox.Refresh();
                
                return result;
            }
            return true;
        }

        /// <summary>
        ///     专给DrawGridLine函数调用
        /// </summary>
        /// <param name="g"></param>
        private void DrawNextGeometricBodyGridLine(Graphics g)
        {
            if (!_drawedNextGbGridLine)
            {
                _drawedNextGbGridLine = true;
                NextgbPictureBox.Size = new Size(Config.NextPerCellSize*4, Config.NextPerCellSize*4);
                //画一个像素宽度为Config.NextHorizontalGridLineWidth，Config.NextHorizontalGridLineColor色的网格横向线，间距为Config.NextPerCellSize;
                //从底部开始绘图
                using (Pen pen =
                    new Pen(
                        Config.NextHorizontalGridLineColor, Config.NextHorizontalGridLineWidth))
                {
                    for (int i = 1; i < Config.Height; ++i)
                    {
                        int y = Config.NextPerCellSize*i - 1;
                        g.DrawLine(pen, new Point(0, y), new Point(NextgbPictureBox.Size.Width, y));
                    }
                }
                //画一个像素宽度为，Config.NextVeritcalGridLineColor色的网格纵向线，间距为Config.NextPerCellSize;
                //从左边开始绘图
                using (Pen pen =
                    new Pen(
                        Config.NextVerticalGridLineColor, Config.NextVerticalGridLineWidth))
                {
                    for (int i = 1; i < Config.Width; ++i)
                    {
                        int x = Config.NextPerCellSize*i - 1;
                        g.DrawLine(pen, new Point(x, 0), new Point(x, NextgbPictureBox.Size.Height));
                    }
                }
            }
        }

        /// <summary>
        ///     绘制网格线
        /// </summary>
        private void DrawGridLine(Graphics g)
        {
            _drawedNextGbGridLine = true;
            Size pbSize = RggPictureBox.Size;
            //每个小格所占用的像素，要求两个值相等
            int iPixelPerCompartmentWidth = pbSize.Width/Config.Width;
            int iPixelPerCompartmentHeight = pbSize.Height/Config.Height;

            using (Brush brush = new SolidBrush(Config.CurrentGbBkColor))
            {
                g.FillRectangle(brush, 0, 0, RggPictureBox.Width, RggPictureBox.Height);
            }
            //画一个像素宽度为Config.CurrentHorizontalGridLineWidth，Config.CurrentHorizontalGridLineColor色的网格横向线，间距为iPixelPerCompartmentHeight;
            //从底部开始绘图
            using (Pen pen =
                new Pen(
                    Config.CurrentHorizontalGridLineColor, Config.CurrentHorizontalGridLineWidth))
            {
                for (int i = 1; i < Config.Height; ++i)
                {
                    //int y = pbSize.Height - i*iPixelPerCompartmentHeight - (i - 1)*Config.CurrentHorizontalGridLineWidth;
                    int y = iPixelPerCompartmentHeight*i - 1;
                    g.DrawLine(pen, new Point(0, y), new Point(pbSize.Width, y));
                }
            }
            //画一个像素宽度为，Config.VeritcalGridLineColor色的网格纵向线，间距为iPixelPerCompartmentWidth;
            //从左边开始绘图
            using (Pen pen =
                new Pen(
                    Config.CurrentVerticalGridLineColor, Config.CurrentVerticalGridLineWidth))
            {
                for (int i = 1; i < Config.Width; ++i)
                {
                    //int x = i*iPixelPerCompartmentWidth + (i - 1)*Config.CurrentVerticalGridLineWidth;
                    int x = iPixelPerCompartmentWidth*i - 1;
                    g.DrawLine(pen, new Point(x, 0), new Point(x, pbSize.Height));
                }
            }
        }
    }

    public enum RggState
    {
        Start,
        Stop,
        Pause,
        GameOver
    };
}